package com.atguigu.gmall.realtime.utils

import java.lang.reflect.{Field, Method, Modifier}

import com.atguigu.gmall.realtime.bean.{DauInfo, PageLog}

import scala.util.control.Breaks

/**
 * 对象属性拷贝工具类, 用于将一个对象中的属性值拷贝到另外一个对象中对应的属性上.
 */
object MyBeanCopy {

  def main(args: Array[String]): Unit = {
    val pageLog: PageLog = PageLog("m1001" , "u1001" , "ar1001" , null ,null ,null ,null ,"vc1001" , null ,null ,null ,null ,null ,100L, null ,123456L)
    val dauInfo: DauInfo = new DauInfo()
    //copyFieldValue(pageLog , dauInfo)
    copyFieldValueByMethod(pageLog , dauInfo)
    println(dauInfo)

  }


  /**
   *
   * 基于get/set方法
   * Scala默认提供的get/set方法:
   * get: fieldname()
   * set: fieldname_$eq( args: fieldType )
   * @param srcObj  被拷贝对象
   * @param destObj 目标对象
   *
   */
  def copyFieldValueByMethod(srcObj : AnyRef ,  destObj : AnyRef ): Unit ={
    if(srcObj == null || destObj == null  ){
      return
    }
    //获取srcObj中所有的属性
    val srcObjFields: Array[Field] = srcObj.getClass.getDeclaredFields
    for (srcField <- srcObjFields) {
      Breaks.breakable{
        //开权限
        //srcField.setAccessible(true)
        //源对象的属性名
        val srcFieldName: String = srcField.getName
        //尝试从目标对象中查找该属性
        val destField: Field = try{
          destObj.getClass.getDeclaredField(srcFieldName)
        }catch {
          case ex : NoSuchFieldException => Breaks.break()
        }
        //判断目标对象的属性是否可写
        if(destField.getModifiers.equals(Modifier.FINAL)){
          Breaks.break()
        }
        //开权限
        //destField.setAccessible(true)

        // 获取源对象的属性值， 赋值给目标对象对应的属性
        //val srcFieldValue: AnyRef = srcField.get(srcObj)
        //destField.set(destObj , srcFieldValue)

        //从srcObj获取srcFieldName对应的get方法
        val srcFieldGetMethodName : String = srcFieldName
        val srcFieldGetMethod: Method = srcObj.getClass.getDeclaredMethod(srcFieldGetMethodName)

        //从destObj获取destFieldName对应的set方法
        val destFieldName: String = destField.getName
        val destFieldSetMethodName : String = destFieldName+"_$eq"
        val destFieldSetMethod: Method = destObj.getClass.getDeclaredMethod(destFieldSetMethodName , destField.getType)

        //获取源对象属性的值, 赋值给目标对象的属性
        val srcFieldValue: AnyRef = srcFieldGetMethod.invoke(srcObj)
        destFieldSetMethod.invoke(destObj , srcFieldValue)

      }
    }
  }

  /**
   *
   * 基于属性
   *
   * @param srcObj  被拷贝对象
   * @param destObj 目标对象
   *
   * 反射很重要.
   */
  def copyFieldValue(srcObj : AnyRef ,  destObj : AnyRef ): Unit ={

    if(srcObj == null || destObj == null  ){
      return
    }
    //获取srcObj中所有的属性
    val srcObjFields: Array[Field] = srcObj.getClass.getDeclaredFields
    for (srcField <- srcObjFields) {
      Breaks.breakable{
        //开权限
        srcField.setAccessible(true)
        //源对象的属性名
        val srcFieldName: String = srcField.getName
        //尝试从目标对象中查找该属性
        val destField: Field = try{
          destObj.getClass.getDeclaredField(srcFieldName)
        }catch {
          case ex : NoSuchFieldException => Breaks.break()
        }

        //判断目标对象的属性是否可写
        if(destField.getModifiers.equals(Modifier.FINAL)){
          Breaks.break()
        }
        //开权限
        destField.setAccessible(true)
        // 获取源对象的属性值， 赋值给目标对象对应的属性
        val srcFieldValue: AnyRef = srcField.get(srcObj)
        destField.set(destObj , srcFieldValue)
      }
    }
  }
}
